#!/usr/bin/python
# -*- coding: utf-8 -*-

#This file is part of pyLot library.
#
# pyLot is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# pyLot is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with pyLot.  If not, see <http://www.gnu.org/licenses/>.

__author__ = u'Pierre Puiseux, Guillaume Baty'
__copyright__ = u"Copyright 2011-2012 (C) andheo, Université de Pau et des Pays de l'Adour"
__credits__ = [u'Pierre Puiseux', u'Guillaume Baty']
__license__ = "GNU Lesser General Public License"

u"""
This module provides GuiMainWindow class, an improved QMainWindow class.
"""

try :
  tr_
except NameError :
  def tr_(s):
    return s

__all__ = [u'GuiMainWindow']

import sys

from Qt4 import QtGui
from Qt4.QtCore import (SIGNAL, QSize, QPoint, Qt)

from pyLot.core import (uprint, Plugin)

from _pylot.qtgui.tools import (create_background_palette,
                                qcolor_to_qstring, qstring_to_qcolor)
from _pylot.qtgui.widgets.gui_autowidget import GuiAutoWidget
from _pylot.qtgui.widgets.gui_autopreferences import GuiAutoPrefs
from _pylot.qtgui.widgets.gui_mainmanager_prefs import GuiMainManagerPrefs
from _pylot.qtgui.widgets.gui_scriptrunner import GuiScriptRunner
from _pylot.qtgui.interfaces.widgetinterface import WidgetInterface
from _pylot.qtgui.qprocesshandler import QProcessHandler

class GuiTabBar (QtGui.QTabWidget, WidgetInterface):
  def __init__ (self, handler=None):
    QtGui.QTabWidget.__init__(self)
    WidgetInterface.__init__(self, handler)

class GuiMainManager (QtGui.QMainWindow, GuiAutoWidget):
  u"""
  GuiMainManager is a QMainWindow specialized for plugins.
  It provides method to dock and place GuiPart automatically.
  """

  def __init__ (self, handler=None, process_widget=None, status_bar=None,
                dock_mode=u'tabs'):
    u"""
    :param handler: optional handler.
    :type handler: :class:`pylot.interfaces.HandlerInterface`
    :param config: 'tabs', 'docks', 'process_and_docks', 'process_and_tabs'
    """
    QtGui.QMainWindow.__init__(self)
    GuiAutoWidget.__init__(self, handler)

    if process_widget is None :
      process_widget = QProcessHandler().getProcessHandlerWidget()
    if status_bar is None :
      status_bar = QProcessHandler().io.getStatusBarWidget()
    self._process_widget = process_widget

    self._docks = []
    self._dock_contents = []
    self._current_docks = []

    self._tabs = []
    self._current_tabs = []

    self._auto_menus = []
    self._default_dock_mode = dock_mode

    self.tab_bar = GuiTabBar(self.handler)
    self.append(process_widget)

    if dock_mode == u'process_and_tabs' :
      self.autoSetToolBars(process_widget)
      self.setCentralWidget(process_widget)
      self.autoSetDocks(self.tab_bar, 'name', 'alias')
    elif dock_mode == u'process_and_docks' :
      self.autoSetToolBars(process_widget)
      self.setCentralWidget(process_widget)
    elif dock_mode == u'docks' :
      self.autoSetWidget(process_widget, name=u'process_handler',
        alias=u'Process Handler', auto_menu=False)
    elif dock_mode == u'tabs' :
      self.setCentralWidget(self.tab_bar)
      self.autoSetWidget(process_widget, name=u'process_handler',
        alias=u'Process Handler', auto_menu=False)

    self.setStatusBar(status_bar)

    self._window_state = None
    self._createActions()
    self._createMenus()
    self._createPreferences()
    self._createConnexions()

    self.pref = None
    self.handler.io.writeStatus(tr_(u'Ready !'))

  def _getPlugin(self, name):
    if isinstance(name, Plugin) :
      name = name.name
    if isinstance(name, (unicode, str)) and name not in self._children :
      uprint(u'Plugin %s not exists' % name, file=sys.stdout)
      return None
    elif isinstance(name, WidgetInterface) :
      return name
    else :
      return self._children[name]

  def autoSetPlugin(self, module, name=None, auto_menu=True, auto_tool_bar=True,
                    auto_prefs=True, alias=None):
    u"""
    :param auto_dock: if True, docks module
    :param auto_menu: if True, adds module's menu to menu bar
    :param auto_tool_bar: if True, adds tool_bar
    """
    module = self._getPlugin(module)
    if name is None :
      name = module.name
    if alias is None :
      alias = name
    if module :
      self.autoSetWidget(module, name, alias, auto_menu, auto_tool_bar,
                         auto_prefs)

  def autoSetDocks(self, widget, name, alias):
    for i, dockable_widget in enumerate(widget.docks) :
      dock_content, dock_level = dockable_widget
      dock_content.level = dock_level
      self.connect(dock_content, SIGNAL('undockRequested'), self.undockWidget)
      self.connect(dock_content, SIGNAL('redockRequested'), self.redockWidget)

      dock_name = dock_content.objectName()
      dock_name = u'dockWidget%s' % dock_name
      dock_alias = dock_content.windowTitle()

      dock = QtGui.QDockWidget(self)
      dock.setFeatures(QtGui.QDockWidget.AllDockWidgetFeatures)
      dock.setObjectName(dock_name)
      dock.setWidget(dock_content)

      if dock_content not in self._dock_contents :
        self._dock_contents.append(dock_content)
        self._docks.append(dock)
      self.addDockWidget_(dock_content)

  def addDockWidget_(self, dock_content, show=True):
    u"""
    :param show: if True, show dock, else only make it available
    """
    if dock_content not in self._current_docks :
      dock = dock_content.parent()
      if dock_content.windowTitle() :
        msg = tr_(u'New dock "%s" is available\n') % dock_content.windowTitle()
      else :
        msg = tr_(u'A New dock is available\n')
      self.handler.io.writeStatus(msg, style=u'warn')
      if show is True :
        dock.show()
      self.addDockWidget(Qt.TopDockWidgetArea, dock)
      self._current_docks.append(dock_content)

  def removeDockWidget_(self, dock_content):
    if dock_content in self._current_docks :
      dock = dock_content.parent()
      dock.hide()
      self.removeDockWidget(dock)
      self._current_docks.remove(dock_content)

  def autoSetTabs(self, widget, name, alias):
    for i, tab_widget in enumerate(widget.docks) :
      tab, tab_level = tab_widget
      tab.level = tab_level
      self.connect(tab, SIGNAL('undockRequested'), self.undockWidget)
      self.connect(tab, SIGNAL('redockRequested'), self.redockWidget)

      if tab not in self._tabs :
        self._tabs.append(tab)

      self.addTab_(tab)

  def addTab_(self, tab):
    if tab not in self._current_tabs :
      tab_name = tab.windowTitle()
      self.tab_bar.addTab(tab, unicode(tab_name))
      self._current_tabs.append(tab)
      self.tab_bar.setCurrentWidget(tab)

  def removeTab_(self, tab):
    if tab in self._current_tabs :
      self.tab_bar.removeTab(self.tab_bar.indexOf(tab))
      self._current_tabs.remove(tab)

  def autoAttachWidget(self, widget, name, alias):
    dock_mode = self._default_dock_mode
    if dock_mode in ('tabs', 'process_and_tabs') :
      self.autoSetTabs(widget, name, alias)
    elif dock_mode in ('docks', 'process_and_docks') :
      self.autoSetDocks(widget, name, alias)

  def autoSetToolBars(self, widget):
    for tool_bar in widget.tool_bars :
      self.addToolBar(tool_bar)

  def autoSetMenus(self, widget):
    for menu in widget.menus :
      name = menu.objectName()
      if name in (u'menu_file', u'menu_edit', u'menu_help') :
        pass
      else :
        self._auto_menus.append(menu)

  def autoSetWidget(self, widget, name, alias, auto_menu=True,
                    auto_tool_bar=True, auto_prefs=True):
    if auto_menu :
      self.autoSetMenus(widget)
    if auto_tool_bar :
      self.autoSetToolBars(widget)
    self.autoAttachWidget(widget, name, alias)

  def autoAddMenus(self):
    self.menuBar().addMenu(self.menu_file)
    self.menuBar().addMenu(self.menu_edit)
    for menu in self._auto_menus :
      self.menuBar().addMenu(menu)
    self.menuBar().addMenu(self.menu_help)

  def undockWidget(self, widget):
    if widget in self._current_docks:
      self.removeDockWidget_(widget)
    elif widget in self._current_tabs :
      self.removeTab_(widget)

  def redockWidget(self, widget, show=True):
    if self._default_dock_mode in ('tabs', 'process_and_tabs') :
      self.addTab_(widget)
    elif self._default_dock_mode in ('docks', 'process_and_docks') :
      self.addDockWidget_(widget, show)

  def showPreferences(self):
    u"""
    Shows children's preferences

    :param name: plugin's name
    :type name: :obj:`unicode`
    """
    self.pref.show()

  def _quit (self):
    running = self.handler.process.getRunningProcess()
    in_queue = self.handler.process.getInQueueProcess()
    if running or in_queue :
      np = len(running)
      nq = len(in_queue)
      box = QtGui.QMessageBox(QtGui.QMessageBox.Critical, tr_(u'Process running'),
        tr_(u'There are %(RUNNING)d running process and %(QUEUE)d in queue') % dict(
        RUNNING=np, QUEUE=nq))
      info = u''
      info += tr_(u"<b>Do you really want to quit</b> ?<br><br>")
      info += tr_(u"Quitting interface don't kill process !<br>")
      info += tr_(u"If you want to kill them, please cancel closing and kill process manually")
      box.setInformativeText(info)
      box.setStandardButtons(QtGui.QMessageBox.Close | QtGui.QMessageBox.Cancel)
      box.setDefaultButton(QtGui.QMessageBox.Cancel)
      script_ids = []
      script_ids.append(tr_(u'Running process'))
      for cg in running :
        script_ids.append(u'  -%s' % cg.alias)
      script_ids.append(tr_(u'Process in queue'))
      for cg, wait in in_queue :
        script_ids.append(u'  -%s' % cg.alias)
      box.setDetailedText(u'\n'.join(script_ids))
      rep = box.exec_()
      if rep == QtGui.QMessageBox.Close :
        self._reallyQuit()
        return True
    else :
      self._reallyQuit()
      return True
    return False

  def _reallyQuit(self):
    for child in self._children.values() :
      child.quit()
    QtCore.QCoreApplication.instance().quit()

  def closeEvent (self, event):
    if self._quit() is True :
      event.accept()
    else :
      event.ignore()

  def _createConnexions (self):
    u"""
    Connects "close" events
    """
    self.connect(QtCore.QCoreApplication.instance(), SIGNAL("lastWindowClosed()"), self.quit)
    self.connect(self.action_preferences, SIGNAL('triggered()'),
                 self.showPreferences)
    self.connect(self.action_retranslate_ui, SIGNAL('triggered()'),
                 self._onActionRetranslateUITriggered)
    self.connect(self.action_quit, SIGNAL('triggered()'),
                 self.quit)
    self.connect(self.action_whatsthis, SIGNAL("triggered()"),
                 QtGui.QWhatsThis.enterWhatsThisMode)
    self.connect(self.action_run_script, SIGNAL("triggered()"),
                 self.runScript)
    self.connect(self.action_run_last_script, SIGNAL("triggered()"),
                 self.runLastScript)

    self.connect(self.main_prefs.getUserLevelChooser(),
                 SIGNAL('dataChanged'), self._onUserLevelChanged)
    self.connect(self.main_prefs.getVerbosityLevelChooser(),
                 SIGNAL('dataChanged'), self._onVerbosityLevelChanged)

  def _createActions(self):
    self.action_preferences = QtGui.QAction(self)
    self.action_quit = QtGui.QAction(self)
    self.action_quit.setShortcut('Ctrl+Q')
    self.action_retranslate_ui = QtGui.QAction(self)
    self.action_whatsthis = QtGui.QAction(self)
    self.action_run_script = QtGui.QAction(self)
    self.action_run_last_script = QtGui.QAction(self)

  def translate(self, locale=None, force=False, recursive=True):
    # Install gettext translators (global and local to current plugin if available)
    # translate handler
    self.handler.translate(locale, force=force, recursive=recursive)
    self.refresh()
    GuiAutoWidget.translate(self, locale, force, recursive)

  def _translateHistoryActions(self):

    script = self.handler.history(u'scriptpaths')
    args = self.handler.history(u'scriptparams')
    if script : # lauhch last script
      self._setTexts([
        (self.action_run_last_script,
        tr_(u'Run last script (%s %s)') % (script, args)),
        ])
    else :
      self._setTexts([
        (self.action_run_last_script, tr_(u'Run last script')),
        ])

  def _translate(self):
    self._setTexts([
      (self.action_preferences, tr_(u'Preferences')),
      (self.action_retranslate_ui, tr_(u'Retranslate GUI')),
      (self.action_quit, tr_(u'Quit')),
      (self.action_whatsthis, tr_(u'What\'s this ?')),
      (self.action_run_script, tr_(u'Run script')),
      (self.action_run_last_script, tr_(u'Run last script')),

      (self.menu_edit, tr_(u'&Edit')),
      (self.menu_file, tr_(u'&File')),
      (self.menu_help, tr_(u'&Help')),
      (self.menu_scripts, tr_(u'&Scripts')),
      ])

    for dock_content in self._current_docks :
      dock_alias = dock_content.windowTitle()
      dock_content.parent().setWindowTitle(dock_alias)

    for idx in range(self.tab_bar.count()):
      tab = self.tab_bar.widget(idx)
      self.tab_bar.setTabText(idx, tab.windowTitle())

    self._setTexts([
      (self.action_whatsthis, tr_(u'Shift+F1')),
      (self.action_run_last_script, tr_(u'Ctrl+Shift+R')),
      ], field=u'shortcut')

    self._translateHistoryActions()

  def _createMenus(self):
    self.menu_edit = QtGui.QMenu()
    self.addMenu_(self.menu_edit)
    self.menu_file = QtGui.QMenu()
    self.addMenu_(self.menu_file)
    self.menu_help = QtGui.QMenu()
    self.addMenu_(self.menu_help)
    self.menu_scripts = QtGui.QMenu()
    self.addMenu_(self.menu_scripts)

  def _createPreferences(self):
    self.pref = GuiAutoPrefs(tabs=True, handler=self.handler)

    self.main_prefs = GuiMainManagerPrefs(
      char_formats=self._process_widget.char_formats,
      char_colors=self._process_widget.char_colors,
      handler=self.handler)
    self.pref.addPrefWidget(self.main_prefs)
    self.pref.addPrefWidget(self._process_widget.pref)

  def _decorate(self):
    self._setIcons([
      (self.action_preferences, u'preferences.png'),
      (self.action_quit, u'pylotExit.png'),
      ])

  def _fillMenus(self, level):
    self._fill(self.menu_scripts, [
      (self.action_run_script, u'devel'),
      (self.action_run_last_script, u'devel'),
      ])

    # menu FILE
    actions = []
    for child in self._children.values() :
      if hasattr(child, u'menu_file') :
        actions += child.menu_file.actions()
    actions.append(self.action_quit)
    self._fill(self.menu_file, actions)

    # menu EDIT
    actions = [
      self.action_preferences,
      self.action_retranslate_ui,
      (u'-', u'devel'),
      (self.menu_scripts, u'devel'),
      u'-',
      self._process_widget.menu_process,
      ]
    for child in self._children.values() :
      if hasattr(child, u'menu_edit') :
        actions += child.menu_edit.actions()
    self._fill(self.menu_edit, actions)

    # menu HELP
    actions = [
      self.action_whatsthis,
      u'-'
      ]
    for child in self._children.values() :
      if hasattr(child, u'menu_help') :
        actions += child.menu_help.actions()
    self._fill(self.menu_help, actions)

  def _onUserLevelChanged(self, level):
    self.switchUserLevel(level)
    self.translate(force=True)
    self.handler.io.write(tr_(u'User interface level switched to "%s" !\n') % level)

  def _onVerbosityLevelChanged(self, level):
    self.handler.io.level = level

  def _onActionRetranslateUITriggered(self):
    self.translate(force=True)
    locale = self.handler.getCurrentLocale()
    self.handler.io.write(tr_(u'User interface retranslated in "%s" !\n') % locale)

  def changeBackgroundColor (self, widget, color):
    u"""
    Change widget background color
    
    :param widget: the widget you want to change background color
    :type widget: :class:`~PyQt4.QtGui.QWidget`
    :param color: background color (QColor) or GuipyLotManager.colors key
    :type color: class:`~PyQt4.QtGui.QColor` or :obj:`unicode`
    """
    color = self.colors[color]
    background = create_background_palette(color)

    widget.setPalette(background)

  def runLastScript(self):
    script = self.handler.history(u'scriptpaths')
    args = self.handler.history(u'scriptparams')
    if script : # lauhch last script
      if args.strip() : args = args.split()
      else : args = []
      self._translateHistoryActions()
      self.handler.runScript(script, args=args, wait=True)
    else : # launch dialog
      self.runScript()

  def runScript(self):
    script_runner = GuiScriptRunner(self.handler.history)
    res = script_runner.exec_()
    if res == QtGui.QDialog.Accepted :
      script = script_runner.getScript()
      args = script_runner.getParametersAsList()
      uargs = u','.join(args)
      self._translateHistoryActions()
      self.handler.runScript(script, args=args, wait=True)

  # read/write settings methods
  #=============================================================================

  def _switchUserLevel(self, level):
    for widget in self._dock_contents + self._tabs :
      try :
        level = widget.level
      except AttributeError :
        level = u'user'
      if self.handler.goodLevel(level):
        self.redockWidget(widget, show=False)
      else :
        self.undockWidget(widget)

  def _writeSettings(self, settings, common):
    u"""
    Writes position, size and state
    
    :param settings: where you want to write
    :type settings: :class:`~PyQt4.QtCore.QSettings`
    """
    settings.beginGroup(u"MainWindow")
    settings.setValue(u"size", QVariant(self.size()))
    settings.setValue(u"pos", QVariant(self.pos()))
    settings.setValue(u"State", QVariant(self.saveState()))
    settings.endGroup()

    settings.beginGroup(u"MainPreferences")
    settings.setValue(u"size", QVariant(self.pref.size()))
    settings.setValue(u"pos", QVariant(self.pref.pos()))
    settings.endGroup()

    common.beginGroup(u"Fonts")
    for key, value in self._process_widget.char_formats.items():
      font = value.font()
      common.setValue(key + u'font', QVariant(font.toString()))
    common.endGroup()

    common.beginGroup(u"Colors")
    for key, fmt in self._process_widget.char_formats.items():
      # foreground
      color = qcolor_to_qstring(fmt.foreground().color())
      common.setValue(u'fg_RGBA_%s' % key,
                             QVariant(color))
      # background
      color = qcolor_to_qstring(fmt.background().color())
      common.setValue(u'bg_RGBA_%s' % key,
                             QVariant(color))

    common.endGroup()

  def _readSettings(self, settings, common):
    u"""
    Reads position, size and state

    :param settings: settings you want to read on.
    :type settings: :class:`~PyQt4.QtCore.QSettings`
    """
    settings.beginGroup(u"MainWindow")
    size = settings.value(u"size", QVariant(QSize(800, 600))).toSize()
    position = settings.value(u"pos", QVariant(QPoint(200, 200))).toPoint()
    state = settings.value(u"State",
      QVariant(self.saveState())).toByteArray()

    if state != self.saveState() :
      # maybe state of widget not yet created was saved, so we keep saved version
      self._window_state = state

    self.restoreState(state)
    self.resize(size)
    self.move(position)
    settings.endGroup()

    settings.beginGroup(u"MainPreferences")
    size = settings.value(u"size", QVariant(QSize(400, 300))).toSize()
    position = settings.value(u"pos", QVariant(QPoint(300, 300))).toPoint()
    self.pref.resize(size)
    self.pref.move(position)
    settings.endGroup()

    common.beginGroup(u"Fonts")
    for key, value in self._process_widget.char_formats.items():
      font = value.font()
      variant = common.value(key + u'font', QVariant(font.toString()))
      font.fromString(variant.toString())
      self._process_widget.char_formats[key].setFont(font)
    common.endGroup()

    common.beginGroup(u"Colors")
    # Foreground colors
    for key, fmt in self._process_widget.char_formats.items():
      color = qcolor_to_qstring(fmt.foreground().color())
      RGBA = common.value(u'fg_RGBA_%s' % key, QVariant(color)).toString()
      fmt.setForeground(QtGui.QBrush(qstring_to_qcolor(RGBA)))

    # Background colors
    for key, fmt in self._process_widget.char_formats.items():
      color = qcolor_to_qstring(fmt.background().color())
      RGBA = common.value(u'bg_RGBA_%s' % key, QVariant(color)).toString()
      fmt.setBackground(QtGui.QBrush(qstring_to_qcolor(RGBA)))
    common.endGroup()
